package com.levi.start;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PropertyResourceBundle;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.levi.core.anno.Builder;
import com.levi.core.anno.GetURL;
import com.levi.utils.PackageScanUtil;

/**
 * 容器初始化的监听器listener
 * 
 * @author cpp 类似springmvc中的contextLoadListener
 */
public class ContextInitListener implements ServletContextListener {
	/**
	 * ioc容器
	 */
	public static Map<String, Object> iocs = new HashMap<>();
	/**
	 * 
	 */
	public static Map<String, Method> urlMaps = new HashMap<>();

	public static String DEFAULT_CONFIG_LOCATION = "IOC_LOCATION";

	/**
	 * 项目启动执行的方法 在springmvc中的web.xml <context-param> name value </context-param>
	 */
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		ServletContext servletContext = sce.getServletContext();
		// 获取本地文件配置的文件名
		String location = servletContext.getInitParameter(DEFAULT_CONFIG_LOCATION);
		// 判断是否为null
		if (location == null) {
			throw new RuntimeException("配置文件不存在");
		}
		// 解析配置文件properties【最简单的配置文件】
		try {
			// 获取配置文件
			PropertyResourceBundle resourceBundle = new PropertyResourceBundle(
					ContextInitListener.class.getClassLoader().getResourceAsStream(location));
			// 获取配置信息
			// 1.包扫描技术
			// 说明：packagescan = com.levi.controller
			String packagescan = resourceBundle.getString("packagescan");
			String[] pkgs = packagescan.split(",");
			List<Class<?>> clazzs = new ArrayList<>();
			for (String pkg : pkgs) { // 扫描包
				clazzs.addAll(PackageScanUtil.scan(pkg));
			}
			// 将有注解的类，创建对象，并且放入容器
			for (Class<?> clazz : clazzs) {
				Builder builder = clazz.getAnnotation(Builder.class);
				if (builder != null) { // 该类需要创建对象,并且需要放入容器
					putObjToIocs(clazz, builder);// 将该对象放入容器里面
					putUrlMapps(clazz);// 将方法的url和方法对应起来
				}
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将url放入map集合，类似于springmvc中的处理映射器和适配器映射器的组合体
	 * 
	 * @param clazz
	 */
	private void putUrlMapps(Class<?> clazz) {
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			GetURL getURL = method.getAnnotation(GetURL.class);
			if (getURL != null) { // @GetURL(value="")
				String url = getURL.value(); // 取出url地址
				urlMaps.put(url, method);
			}
		}
	}

	/**
	 * 模拟：把所有的对象放进IOC容器
	 * 
	 * @param clazz
	 * @param builder
	 */
	private void putObjToIocs(Class<?> clazz, Builder builder) {
		Object object = null;
		try {
			object = clazz.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		String name = builder.name(); // 获取该实例的名称
		if (name.equals("")) { // UserService- > userService
			name = clazz.getSimpleName();
		}
		iocs.put(name, object); // 将对象放入容器
	}

	/**
	 * 项目结束时执行的方法
	 */
	@Override
	public void contextDestroyed(ServletContextEvent sce) {

	}

}
